package ch.ethz.fcl.metrobuzz.controller.gui.seekbar;

import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;

import ch.ethz.fcl.metrobuzz.controller.external.ActionEvent;
import ch.ethz.fcl.metrobuzz.controller.external.mouse.MouseEvent;
import ch.ethz.fcl.metrobuzz.controller.external.mouse.MouseListener;
import ch.ethz.fcl.metrobuzz.data.MBData;
import ch.ethz.fcl.metrobuzz.render.interchange.InterchangeRender;
import ch.ethz.fcl.metrobuzz.render.techniques.colors.MBColorTable;
import ch.ethz.fcl.metrobuzz.render.techniques.projection.Projection;
import ch.ethz.fcl.metrobuzz.spatialindex.object2d.Point2D;
import ch.ethz.fcl.metrobuzz.spatialindex.object2d.Rectangle;
import ch.ethz.fcl.metrobuzz.spatialindex.object3d.Point3D;
import ch.ethz.fcl.metrobuzz.tools.MBGL;
import ch.ethz.fcl.metrobuzz.viewer.MBViewer;

public class SeekBar {
	private Orientation orientation = Orientation.Horizontal;

	private int x, y;
	private int hist_min, hist_max;
	private int width, height;

	private int min, max;
	private int absoluteMinValue, absoluteMaxValue;

	private float normalizedMinValue = 0;
	private float normalizedMaxValue = 1;

	private SeekBarThumb minThumb;
	private SeekBarThumb maxThumb;

	private SeekBarThumb selectedThumb;

	public SeekBar(int absoluteMinValue, int absoluteMaxValue) {
		minThumb = new SeekBarThumb();
		maxThumb = new SeekBarThumb();

		this.absoluteMinValue = absoluteMinValue;
		this.absoluteMaxValue = absoluteMaxValue;
	}

	public void update(MouseEvent event, MBViewer view) {
		invalidate();

		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glPushMatrix();
		GL11.glLoadIdentity();

		GL11.glOrtho(0, Display.getWidth(), 0, Display.getHeight(), -1, 1);
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glPushMatrix();
		GL11.glLoadIdentity();

		if (orientation == Orientation.Horizontal) {
			GL11.glTranslatef(x, y, 0);

			GL11.glRotated(0, 1, 0, 0);
			GL11.glRotated(0, 0, 1, 0);
			GL11.glRotated(-90, 0, 0, 1);

			GL11.glTranslatef(-x, -y, 0);
		}

		selecteThumb(event, view);
		if (selectedThumb != null) {
			MouseListener.update = false;
		} else if (MouseListener.update) {
			MouseListener.update = true;
		}

		render();

		GL11.glPopMatrix();
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glPopMatrix();
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
	}

	public void setMin(int min) {
		this.min = min;
		normalizedMinValue = (float) (min - absoluteMinValue)
				/ (absoluteMaxValue - absoluteMinValue);
	}

	public void setMax(int max) {
		this.max = max;
		normalizedMaxValue = (float) (max - absoluteMinValue)
				/ (absoluteMaxValue - absoluteMinValue);
	}

	public int getMin() {
		return min;
	}

	public int getMax() {
		return max;
	}

	public void setPosition(int x, int y) {
		this.x = x;
		this.y = y;
	}

	public void setSize(int width, int height) {
		this.width = width;
		this.height = height;
		minThumb.setWidth(width);
		maxThumb.setWidth(width);
		minThumb.setBound(new Rectangle(x, y, x + width, y + height));
		maxThumb.setBound(new Rectangle(x, y, x + width, y + height));
	}

	private void invalidate() {
		min = (int) ((absoluteMaxValue - absoluteMinValue) * normalizedMinValue)
				+ absoluteMinValue;
		max = (int) ((absoluteMaxValue - absoluteMinValue) * normalizedMaxValue)
				+ absoluteMinValue;

		hist_min = min;
		hist_max = max;

		minThumb.setPosition(x, y + height * normalizedMinValue);
		maxThumb.setPosition(x, y + height * normalizedMaxValue);
	}

	private void selecteThumb(MouseEvent me, MBViewer view) {
		// mouse down check which thumb is pressed
		if (me.getAction() == ActionEvent.ACTION_DOWN && selectedThumb == null) {
			Projection proj = new Projection();
			Point3D mouseP = proj.getWorldCoord(
					new Point2D(Mouse.getX(), Mouse.getY()), 0);

			boolean minPressed = minThumb.getThumbArea().contains(
					mouseP.getX(), mouseP.getY());
			boolean maxPressed = maxThumb.getThumbArea().contains(
					mouseP.getX(), mouseP.getY());
			if (minPressed && !maxPressed) {
				selectedThumb = minThumb;
			} else if (!minPressed && maxPressed) {
				selectedThumb = maxThumb;
			} else if (minPressed && maxPressed) {
				// if both thumbs are pressed (they lie on top of each other),
				// choose the one with more room to drag. this avoids "stalling"
				// the
				// thumbs in a corner, not being able to drag them apart
				// anymore.
				float part = 0;
				if (orientation == Orientation.Horizontal)
					part = (float) (Mouse.getX() - x) / height;
				else
					part = (float) (Mouse.getY() - y) / height;
				selectedThumb = (part > 0.5f) ? minThumb : maxThumb;
			}
		} else if (me.getAction() == ActionEvent.ACTION_DRAG
				&& selectedThumb != null) {
			float delta = 0;
			if (orientation == Orientation.Horizontal)
				delta = me.getEventX() - me.getPreEventX();
			else
				delta = me.getEventY() - me.getPreEventY();
			if (selectedThumb == maxThumb) {
				maxThumb.setPosition(maxThumb.getPosX(), maxThumb.getPosY()
						+ delta);
				normalizedMaxValue = (float) (maxThumb.getPosY() - y) / height;
				normalizedMaxValue = Math.min(normalizedMaxValue, 1);
				normalizedMaxValue = Math.max(normalizedMinValue,
						normalizedMaxValue);
			} else if (selectedThumb == minThumb) {
				minThumb.setPosition(minThumb.getPosX(), minThumb.getPosY()
						+ delta);
				normalizedMinValue = (float) (minThumb.getPosY() - y) / height;
				normalizedMinValue = Math.max(normalizedMinValue, 0);
				normalizedMinValue = Math.min(normalizedMinValue,
						normalizedMaxValue);
			}

			min = (int) ((absoluteMaxValue - absoluteMinValue) * normalizedMinValue)
					+ absoluteMinValue;
			max = (int) ((absoluteMaxValue - absoluteMinValue) * normalizedMaxValue)
					+ absoluteMinValue;

			if (min != hist_min || max != hist_max) {
				MBData.startTime = min;
				MBData.period = max - min;
				InterchangeRender.UPDATE = true;
			}
		} else if (me.getAction() == ActionEvent.ACTION_UP) {
			selectedThumb = null;
			if (view != null)
				MBViewer.newUpdate = true;
		}
	}

	private void renderRange() {
		GL11.glLineWidth(1.0f);
		GL11.glColor4f(0, 0, 0, 1);
		GL11.glBegin(GL11.GL_LINE_STRIP);
		GL11.glVertex3f(x + width * 4 / 15, y, 0);
		GL11.glVertex3f(x + width * 10 / 15, y, 0);
		GL11.glVertex3f(x + width * 10 / 15, y + height, 0);
		GL11.glVertex3f(x + width * 4 / 15, y + height, 0);
		GL11.glVertex3f(x + width * 4 / 15, y, 0);
		GL11.glEnd();

		MBColorTable.assignColor(MBColorTable.LIGHTGREY, 1.0f);
		GL11.glBegin(GL11.GL_QUADS);
		GL11.glVertex3f(x + width * 4 / 15, y, 0);
		GL11.glVertex3f(x + width * 10 / 15, y, 0);
		GL11.glVertex3f(x + width * 10 / 15, y + height, 0);
		GL11.glVertex3f(x + width * 4 / 15, y + height, 0);
		GL11.glEnd();

		GL11.glColor4f(251.0f / 255, 194.0f / 255, 27.0f / 255, 1.0f);
		GL11.glBegin(GL11.GL_QUADS);
		GL11.glVertex3f(x + width * 4 / 15, y + normalizedMinValue * height, 0);
		GL11.glVertex3f(x + width * 10 / 15, y + normalizedMinValue * height, 0);
		GL11.glVertex3f(x + width * 10 / 15, y + normalizedMaxValue * height, 0);
		GL11.glVertex3f(x + width * 4 / 15, y + normalizedMaxValue * height, 0);
		GL11.glEnd();
	}

	private void render() {
		MBGL.enableBlend();

		GL11.glEnable(GL11.GL_SCISSOR_TEST);
		if (orientation == Orientation.Vertical) {
			GL11.glScissor(x + width * 4 / 15 - 1, y - 1, width * 6 / 15 + 2,
					height + 2);
		} else {
			GL11.glScissor(x - 1, y - width - 1,
					height + (int) maxThumb.getHeight(), width + 2);

			GL11.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
			GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

			GL11.glScissor(x + (int) minThumb.getTextureArea().getMinY() - y
					- 1, y - width - 1, (int) minThumb.getTextureArea()
					.getHeight() + 2, (int) minThumb.getTextureArea()
					.getWidth() + 2);

			GL11.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
			GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

			GL11.glScissor(x + (int) maxThumb.getTextureArea().getMinY() - y
					- 1, y - width - 1, (int) maxThumb.getTextureArea()
					.getHeight() + 2, (int) maxThumb.getTextureArea()
					.getWidth() + 2);

			GL11.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
			GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
		}

		GL11.glDisable(GL11.GL_SCISSOR_TEST);

		renderRange();

		GL11.glColor4f(1, 1, 1, 1);
		if (selectedThumb == minThumb)
			minThumb.render(SeekBarThumb.thumbPressedTexture);
		else
			minThumb.render(SeekBarThumb.thumbTexture);

		if (selectedThumb == maxThumb)
			maxThumb.render(SeekBarThumb.thumbPressedTexture);
		else
			maxThumb.render(SeekBarThumb.thumbTexture);

		GL11.glDisable(GL11.GL_BLEND);
	}

	public enum Orientation {
		Vertical, Horizontal;
	}
}
